﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Database.Classes
{
    public class Equations
    {
        readonly static List<int> sci = new List<int> { 29, 74, 138, 221, 325, 451, 603, 780, 986, 1223, 1492, 1796, 2138, 2520, 2945, 3415, 3934, 4595, 5131, 5816, 6563, 7377, 8261, 9221, 10260, 11382, 12595, 13901, 15400, 16756, 18377, 20131, 22055, 23950, 26119, 28335, 30404, 33453, 36248, 39076 };
        readonly static List<int> cul = new List<int>() { 0, 318, 765, 1315, 2084, 2985, 4059, 5320, 6785, 8467, 10382, 12547, 14979, 17697, 20721, 24071, 27768, 31835, 36298, 31179, 46507, 52310, 58616, 65454, 72863 };
        public static int SbAV(int l)
        {
            return (int)Math.Ceiling(Math.Pow(l, 1.5) * (l * 1.0));
        }
        public static int SbDV(int l)
        {
            return (int)Math.Ceiling((Math.Pow(l, 2) + l * 7.0) * (l * 1.0) / 2.0);
        }
        public static int BuildingCost(int level)
        {
            return (int)Math.Ceiling(5 * Math.Pow(1.5, level - 1));
        }
        public static int PlayerLevel(int exp)
        {
            long sum = ExpNeededForLevel(1);
            for (int i = 1; ; i++)
            {
                if (exp < sum)
                    return i - 1;
                sum += ExpNeededForLevel(i);
            }
        }
        public static int ExpNeededForLevel(int level)
        {
            return 5 * (int)Math.Ceiling(Math.Pow(level, 2.7) - Math.Pow(level - 1, 2.7));
        }
        public static int PopulationLevel(int level)
        {
            if (level < 1) return 0;
            if (level <= 2) return 21;
            return PopulationLevel(level - 1) + 18 * (level - 1);
        }
        /// <summary>
        /// Returns travel time in h
        /// </summary>
        /// <param name="xa"></param>
        /// <param name="xb"></param>
        /// <param name="ya"></param>
        /// <param name="yb"></param>
        /// <param name="IdA"></param>
        /// <param name="IdB"></param>
        /// <param name="own"></param>
        /// <param name="energy"></param>
        /// <param name="speed"></param>
        /// <returns></returns>
        public static double TravelTime(int xa, int xb, int ya, int yb, int IdA, int IdB, int own, int energy, int speed)
        {
            double dist = 3 * Math.Sqrt(Math.Pow(xb - xa, 2.0) + Math.Pow(yb - ya, 2.0));
            double pDist = 2 * Math.Abs(IdB - IdA);
            return 0.5 + Math.Pow(0.5, own) * Math.Pow(0.91, energy) * Math.Sqrt(dist + pDist + 1) * Math.Pow(15 - speed, 1.05);
            //=0,5+SQRT(3*$B18+2*$C18+1)*POWER(15-$I$1;1,05)*POWER(0,91;$A18)
            //const double A = 1155.0;
            //const double B = 14.15;
            //const double C = 115.75;
            //double dist = A * Math.Sqrt(Math.Pow(xb - xa, 2.0) + Math.Pow(yb - ya, 2.0));
            //double pDist = C * Math.Abs(IdB - IdA);
            //return Math.Pow(0.5, own) * Math.Pow(0.91, energy) * (Math.Sqrt(dist + pDist) + B) + (1 - own) * (5 - speed) / 5;
        }
        public static int ScienceCost(int level)
        {
            if (level < sci.Count)
                return sci[level];
            else
                return sci[sci.Count - 1] + (level - (sci.Count - 1)) * 25000;
        }
        public static int CultureCost(int level)
        {
            if (level < cul.Count)
                return cul[level];
            else
                return cul[cul.Count - 1] + (level - (cul.Count - 1)) * 12000;
        }


        private const int DsAV = 2, DsDV = 1;
        private const int CrAV = 8, CrDV = 16;
        private const int BsAV = 34, BsDV = 24;
        /// <summary>
        /// Returns true if attacker(1) wins else false
        /// </summary>
        /// <param name="A1"></param>
        /// <param name="D1"></param>
        /// <param name="Phy1"></param>
        /// <param name="Math1"></param>
        /// <param name="Ds1"></param>
        /// <param name="Cr1"></param>
        /// <param name="Bs1"></param>
        /// <param name="A2"></param>
        /// <param name="D2"></param>
        /// <param name="Phy2"></param>
        /// <param name="Math2"></param>
        /// <param name="Ds2"></param>
        /// <param name="Cr2"></param>
        /// <param name="Bs2"></param>
        /// <returns></returns>
        public static  bool AttackProcedure(int A1, int D1, int Phy1, int Math1, ref  int Ds1, ref int Cr1, ref int Bs1, int A2, int D2, int Phy2, int Math2, ref int Ds2, ref int Cr2, ref int Bs2, ref double SB, out int attCv, out int defCV, out double attchance, out double winninglosses)
        {
            attCv = Ds1 * (DsAV + DsDV) + Cr1 * (CrAV + CrDV) + Bs1 * (BsAV + BsDV);
            defCV = Ds2 * (DsAV + DsDV) + Cr2 * (CrAV + CrDV) + Bs2 * (BsAV + BsDV) +SbAV((int)SB) + SbDV((int)SB);
            double bonusPhy1, bonusPhy2, bonusMath1, bonusMath2;
            bonusPhy1 = 1 + (Phy1 - Phy2) / 100;
            bonusPhy2 = 1 + (Phy2 - Phy1) / 100;
            bonusMath1 = 1 + (Math1 - Math2) / 100;
            bonusMath2 = 1 + (Math2 - Math1) / 100;
            if (Phy1 >= Phy2 + 6) bonusPhy1 += 0.25;
            if (Math1 >= Math2 + 6) bonusMath1 += 0.25;
            if (Phy2 >= Phy1 + 6) bonusPhy2 += 0.25;
            if (Math2 >= Math1 + 6) bonusMath2 += 0.25;
            //calculate winner
            bool winner = false;

            int CVModified1 = (int)((Ds1 + Cr1 * 8.5 + Bs1 * 21) * (21 + 2 * A1) * bonusPhy1);
            int CVModified2 = (int)((Ds2 + Cr2 * 8.5 + Bs2 * 21 + SbAV((int)SB)) * (21 + 2 * A2) * bonusPhy2);

            int attackDif1 = A1 - A2; int attackDif2 = A2 - A1;
            int CVNeededFor100Percent1 = (int)(CVFactor(attackDif1) * CVModified2);
            int CVNeededFor100Percent2 = (int)(CVFactor(attackDif2) * CVModified1);

            //if att or def has more fleet we have a winner
            if (CVModified1 > CVNeededFor100Percent1)
            {
                winner = true;//attacker wins
                attchance = 1;
                //calc loses
            }
            else if (CVModified2 > CVNeededFor100Percent2)
            {
                winner = false;//def wins
                attchance = 0;
            }
            else
            {
                double chance = Math.Max(CVModified1 * 1.0 / CVNeededFor100Percent1, 0.0);
                if (chance < 0.01) chance = 0;
                if (chance > 1) chance = 1;
                attchance = chance;
                Random r = new Random();
                int l = r.Next(100);
                if (l <= (int)(chance * 100))
                {//att wins
                    winner = true;
                }
                else
                {//def wins
                    winner = false;
                }
            }
            //calc loses
            if (winner == true)
            {
                double av = (Ds2 + 8 * Cr2 + 24 * Bs2 + SbAV((int)SB)) * (5 + A2) * bonusPhy2;
                double dv = (Ds1 * 3 + Cr1 * 24 + Bs1 * 60) * (5 + D1) * bonusMath1;
                double losses = av / dv;
                double m = Math.Max(1 - losses, 0);
                Ds1 = (int)(Ds1 * m); Ds2 = 0;
                Cr1 = (int)(Cr1 * m); Cr2 = 0;
                Bs1 = (int)(Bs1 * m); Bs2 = 0; SB = 0;
                winninglosses = losses;
            }
            else
            {
                double av = (Ds1 + 8 * Cr1 + 24 * Bs1) * (5 + A1) * bonusPhy1;
                double dv = (Ds2 * 3 + Cr2 * 24 + Bs2 * 60 + SbDV((int)SB)) * (5 + D2) * bonusMath2;
                double losses = av / dv;
                double m = Math.Max(1 - losses, 0);
                Ds2 = (int)(Ds2 * m); Ds1 = 0;
                Cr2 = (int)(Cr2 * m); Cr1 = 0;
                Bs2 = (int)(Bs2 * m); Bs1 = 0; SB *= m;
                winninglosses = losses;

            }
            return winner;
        }

        private static double CVFactor(int attackDif1)
        {
            double t = 2;
            if (attackDif1 > 0)
            {
                for (int i = 1; i <= attackDif1; i++)
                    t = t * 0.84;
            }
            else if (attackDif1 < 0)
            {
                for (int i = -1; i >= attackDif1; i--)
                    t = t * 1.07;
            }
            return t;
        }
        //private static void DealDamange(double AV, double def, int math, ref int d, ref int c, ref int b)
        //{
        //    Random r = new Random();
        //    int i = 0;
        //    while (AV > 0)
        //    {
        //        switch (i)
        //        {
        //            case 0:
        //                if (d > 0)
        //                {
        //                    AV -= DsDV * def;
        //                    if (r.Next(100) >= math / 1.5)
        //                        d--;
        //                }
        //                break;
        //            case 1:
        //                if (c > 0)
        //                {
        //                    AV -= CrDV * def; if (r.Next(100) >= math / 2)
        //                        c--;
        //                }
        //                break;
        //            case 2:
        //                if (b > 0)
        //                {
        //                    AV -= BsDV * def;
        //                    if (r.Next(100) >= math / 2)
        //                        b--;
        //                }
        //                break;
        //        }
        //        i++;
        //        i = i % 3;
        //        if (d == 0 && c == 0 && b == 0)
        //            return;
        //    }
        //}
        //private static void DealDamange(double AV, double def, int math, ref int d, ref int c, ref int b, ref double s)
        //{
        //    Random r = new Random();
        //    int i = 0;
        //    while (AV > 0)
        //    {
        //        switch (i)
        //        {
        //            case 0:
        //            case 1:
        //                if (s > 0)
        //                {
        //                    AV -= SbDV((int)s);
        //                }
        //                break;
        //            case 2:
        //                if (d > 0)
        //                {
        //                    AV -= DsDV * def;
        //                    if (r.Next(100) >= math / 1.5)
        //                        d--;
        //                }
        //                break;
        //            case 3:
        //                if (c > 0)
        //                {
        //                    AV -= CrDV * def; if (r.Next(100) >= math / 2)
        //                        c--;
        //                }
        //                break;
        //            case 4:
        //                if (b > 0)
        //                {
        //                    AV -= BsDV * def;
        //                    if (r.Next(100) >= math / 2)
        //                        b--;
        //                }
        //                break;
        //        }
        //        i++;
        //        i = i % 5;
        //        if (d == 0 && c == 0 && b == 0 && s == 0)
        //            return;
        //    }
        //}
    }
}
